/*
 * @desc: asdu构造类, 全部厂家通用
 *
 * */

#ifndef PACKETCONSTRUCTER_H
#define PACKETCONSTRUCTER_H

#include <vector>
#include <QDebug>
#include "IEC103Type.h"
#include <QString>
#include <QByteArray>
#include <mutex>
#include "utils.h"

class PacketConstructer
{
public:
    PacketConstructer *getSinglePacketConstructer(void)
    {
        if(m_instance == nullptr)
        {
              if (m_instance == NULL)
              {
                  std::lock_guard<std::mutex> lockGuard(m_mutexProtoBuffer);
                  if (m_instance == NULL)//第二层校验，为了避免多次实例化
                  {
                      m_instance = new PacketConstructer();
                  }
              }

            m_instance = new PacketConstructer;
        }

        return m_instance;
    }

    // 根据设置的 cpu地址号，组号，选定的数据类型来构造 asdu
    ASDUStruct constructGetBillValueData(QString &addr, QString & groupNo, const QString &datasType);

    ASDUStruct constructGetBillArea(QString &addr);  // 读取系统组的当前定值区号 00组 02条目

    ASDUStruct constructSelectArea(QString &addr, QString &sysGroupNo, QString & ginNo, QString &billArea);

    QString selectYbGroupNo(QString &packet, QString& groupName);       // 根据标题查看压板组的组号
    ASDUStruct constructSelectYBGroupName();
    ASDUStruct constructGetBillValueNames(const QString &addr, const QString & groupNo);

    QString asduToQString(ASDUStruct& asdu);    // 将ASDU结构体转换为QString类型
    QByteArray asduToQByteArray(ASDUStruct& asdu);    // 将ASDU结构体转换为QBytEArray类型

    /*
     * @Desc: 构造总召唤指令字符串
     * @Return: 总召唤指令字符串
     */
    QString constructGetAllCmd();

    // 1.构造主站发送的读命令
    ASDUStruct constructReadAllTitles(QString& applyServiceAddr);  // 构造读所有组的标题，得到组名字和对应的组号
    ASDUStruct constructReadGroupValueCmd(QString& groupNo);     // 构造一个组的读取命令
    ASDUStruct constructReadOneGinNoCmd(QString& ginNo);    // 构造一个条目的读取命令,设置kod值来选定定值名，实际值，单位，步长，最大最小范围

    //2.构造主站修改单个定值的通用数据报文
    ASDUStruct constructModifyWithSure(QString& group, QString&  ginNo, QString& dataValue);       // 带确认的修改单个定值
    ASDUStruct constructModifyWithExecute(QString& ginNo);    // 修改执行, 只需要将 inf 由"f9"改为 "fA"

    //3.构造主站修改多个定值通用数据报文，不具体到单个条目使用ginNo ="00"， 定值为一个组03H， 软压板为一个组0E
    ASDUStruct constructModifyMultilWithSure(const QString& group, QList<QString>& ginNoList, QList<QString>& typeList, QList<QString>& widthList,QList<QString>& valueList);
    ASDUStruct constructModifyMultilWithExecute(const QString& group, QList<QString>& ginNoList, QList<QString>& typeList, QList<QString>& widthList, QList<QString>& valueList);   // 接收发送确认构造的报文命令作为参数

    /*
     * @构造一个数据集元素，包含组号，条目号，描述类别01，数据类型，数据宽度，数据个数，数据
     * @ 组号在定值展示界面提取，每组表格的第一行为组号信息，
     * */
    OneDataSetStruct constructDataSet(uchar groupNo, uchar ginNo, uchar dataType, uchar dataWidth, uchar dataNum, QString data);   // kod == 0x01
    QByteArray constructDataSetByteArray(uchar groupNo, uchar ginNo, uchar dataType, uchar dataWidth, uchar dataNum, QByteArray data);
    /*
     * @Desc：构造一个组的数据集全部元素 所有的GDD和GIN, 修改之前先读定值的值，会拿到一个现有在用的dataSets，在这个现有dataSets的基础之上修改 每个dataset的m_dataValue即可
     * @Para： 组号，条目号，条目的实际数据类型这三个均是读取定值组的定值获取到的，valueList 由用户修改提供
     * @retur： 返回一个数据集包含全部的需要发送的数据
     * */
    QList<OneDataSetStruct> constructDataSets(const QString& group, QList<QString>& ginNoList, QList<QString>& typeList,QList<QString>& WidthList, QList<QString>& valueList);

    void increaseRii(QString& rii);  // 每次发送一条报文加1,直到"FF", 将rii置"00"

private:
    QString m_rii = ASDU_RII;       // 返回信息标识符号，每次发送加1，如果等于"FF"，重置回“00”
    static std::mutex m_mutexProtoBuffer;
    static PacketConstructer* m_instance;
    PacketConstructer();
};

#endif // PACKETCONSTRUCTER_H
